<?php

namespace app\api\service;

use app\api\model\Order as ModelOrder;
use app\api\model\OrderProduct;
use app\api\model\Product as ModelProduct;
use app\api\model\UserAddress;
use app\lib\exception\OrderException;
use app\lib\exception\UserException;
use Exception;
use think\Db;

class Order
{
    //@$oProducts 用户下单的商品信息  用户传递过来的数据
    //@$products  用户购买订单在数据库中实际信息（库存量）
    //@$uid       下单用户id
    protected $oProducts;
    protected $products;
    protected $uid;


    /**
     * @place  用户下单方法
     *
     * @param [type] $uid 用户id
     * @param [type] $oProducts 用户下单的商品信息
     * @return void
     * 
     * 1.  oProducts与products对比
     * 2.  把products从数据库查询出来
     * 3.  赋值以上定义（protected）的参数
     */
    public function place($uid, $oProducts)
    {
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        $this->uid = $uid;
        $status = $this->getOrderStatus();

        if (!$status['pass']) {
            $status['order_id'] = -1;
            return $status;
        }

        //创建订单快照
        $orderSnap = $this->snapOrder($status);
        //生成订单
        $order = $this->createOrder($orderSnap);
        //生成订单后，给订单添加状态
        $order['pass'] = true;
        return $order;
    }


    /**
     * @createOrder 创建订单，把订单信息写入数据库
     *
     * @param [type] $snap 订单快照需要的信息 snapOrder 方法中
     * @return 订单编号，订单ID，订单创建时间
     */
    private function createOrder($snap)
    {
        Db::startTrans();
        try {
            //在 order 表插入数据
            $orderNo = $this->makeOrderNo();
            $order = new ModelOrder();
            $order->user_id = $this->uid;
            $order->order_no = $orderNo;
            $order->total_price = $snap['orderPrice'];
            $order->snap_img = $snap['snapImg'];
            $order->snap_name = $snap['snapName'];
            $order->total_count = $snap['totalCount'];
            $order->snap_address = $snap['snapAddress'];
            $order->snap_items = json_encode($snap['pStatus']);
            //插入数据表
            $order->save();

            //在 order_Product 表插入数据
            //create_time 下单时间
            $orderID = $order->id;
            $create_time = $order->create_time;
            //给每一个商品加上order_id,修改数组中的数据需要加上 “&” 符号
            foreach ($this->oProducts as &$p) {
                $p['order_id'] = $orderID;
            }
            $orderProduct = new OrderProduct();
            $orderProduct->saveAll($this->oProducts);
            Db::commit();
            return [
                'oder_no' => $orderNo,
                'oder_id' => $orderID,
                'create_time' => $create_time
            ];

        } catch (Exception $ex) {
            Db::rollback();
            throw $ex;
        }
    }

    /**
     * @makeOrderNo 生成订单编号方法，一组随机字符串
     *
     * @return 订单编号（年份.月份.日.时间戳.时间戳微秒数.0-99取随机数）
     */
    public static function makeOrderNo()
    {
        //dechex() 十进制转换为十六进制
        //strtoupper()  所有字符转换为大写
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $orderSn =
            $yCode[intval(date('Y')) - 2020] . strtoupper(dechex(date('m'))) . date('d')
            . substr(time(), -5) . substr(microtime(), 2, 5)
            . sprintf('%02d', rand(0, 99));
        return $orderSn;
    }


    /**
     * @snapOrder   生成订单快照方法
     *
     * @param [type] $status
     * @return 订单快照需要的信息
     */
    private function snapOrder($status)
    {
        $snap = [
            // orderPrice     订单总价格 (单价x数量+单价x数量)
            // totalCount     订单总数量
            // pStatus        商品的详细信息
            // snapAddress    订单收货地址(序列化地址，原因地址是数组)
            // snapName       订单名称
            // snapImg        订单图片
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatus' => [],
            'snapAddress' => null,
            'snapName' => '',
            'snapImg' => ''
        ];

        $snap['orderPrice'] = $status['orderPrice'];
        $snap['totalCount'] = $status['totalCount'];
        $snap['pStatus'] = $status['pStatusArray'];
        $snap['snapAddress'] = json($this->getUserAddress());
        $snap['snapName'] = $this->products[0]['snapName'];
        $snap['snapImg'] = $this->products[0]['main_img_url'];

        if (count($this->products) > 1) {
            $snap['snapName'] .= '等';
        }
        return $snap;
    }


    /**
     * @getUserAddress  获取用户地址
     *
     * @return 该订单单的用户地址
     */
    private function getUserAddress()
    {
        $userAddress = UserAddress::where('user_id', '=', $this->uid)->find();
        if (!$userAddress) {
            throw new UserException([
                'msg' => '收货地址不存在，请添加地址后再下单',
                'errorCode' => '60001',
            ]);
        }
        return $userAddress->toArray();
    }


    /**
     * Summary of checkOrderStock
     * 通过订单ID查询订单信息，并进行库存检测
     * @return 
     */
    public function checkOrderStock($orderID)
    {
        $oProducts = OrderProduct::where('order_id', '=', $orderID)->select();
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        $status = $this->getOrderStatus();
        return $status;
    }


    /**
     * @getOrderStatus 获取订单状态，根据 @getProductStatus 方法判断
     *
     * @return 订单状态、订单金额、订单商品数量、订单中商品详细信息
     */

    private function getOrderStatus()
    {
        // @$status       返回客户端结果
        $status = [
            // pass           订单状态
            // orderPrice     订单总价格 (单价x数量+单价x数量)
            // totalCount     订单商品数量
            // pStatusArray   订单中每个商品的详细信息 
            'pass' => true,
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatusArray' => []
        ];

        /*
        取出 @oProducts 中某一个元素 @$oProduct
        @$oProduct 包含 product_id 及 count
        */
        foreach ($this->oProducts as $oProduct) {
            $pStatus = $this->getProductStatus(
                $oProduct['product_id'],
                $oProduct['count'],
                $this->products
            );
            if (!$pStatus['havaStock']) {
                $status['pass'] == false;
            }
            $status['orderPrice'] += $pStatus['totalPrice'];
            $status['totalCount'] += $pStatus['count'];
            array_push($status['pStstusArray'], $pStatus);
        }
        return $status;
    }



    /**
     * @getProductStatus 获取订单中每个商品的状态，只要有一个商品库存不足，订单状态就不通过
     *
     * @param [type] $oPID      用户订单中的商品ID
     * @param [type] $count     该ID商品中的商品数量
     * @param [type] $products  用户提交的订单在数据库中真实信息
     * @return 商品id、商品库存量状态、商品购买数量、 商品名字、 商品订单的价格(单价x数量)
     * 
     * 1. 保存商品的详细信息 $pStatus
     * 2. 循环 $products 下标，取得 $oPID 在 $products 中的序号
     * 3. 判断用户传入的商品ID是否存在
     */

    private function getProductStatus($oPID, $oCount, $products)
    {
        // @$pIndex 商品在订单中的顺序；$oPID 在 $products中每一类商品的序号(购买了几类商品序号就有几个123456)
        $pIndex = -1;

        // $pStatus  定义商品格式，返回的详细信息
        $pStatus = [
            // id               商品的id
            // haveStock        商品库存量状态
            // count            商品购买数量
            // name             商品名字
            // totalPrice       商品订单的价格(单价x数量)
            'id' => null,
            'haveStock' => false,
            'count' => 0,
            'name' => '',
            'totalPrice' => 0
        ];

        for ($i = 0; $i < count($products); $i++) {
            //count($products) 表示在订单中商品类别的总和
            if ($oPID == $products[$i]['id']) {
                $pIndex = $i;
            }
        }

        if ($pIndex == -1) {
            //客户端传递的商品id有可能不存在
            throw new OrderException([
                'msg' => 'id' . $oPID . '的商品不存在，创建订单失败'
            ]);
        } else {
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['name'] = $product['name'];
            $pStatus['count'] = $oCount;
            $pStatus['totalPrice'] = $product['price'] * $oCount;

            if ($product['stock'] - $oCount >= 0) {
                $pStatus['haveStock'] = true;
            }
        }
        return $pStatus;
    }



    /**
     * @getProductsByOrder 用户购买的订单在数据库中信息
     *
     * @param [type] $oProducts 用户下单的商品信息
     * @return  返回该订单在数据库中真实信息
     * 
     * 1.  循环用户下单的商品信息($oProducts)中每一件商品ID(product_id)
     * 2.  把每一件商品ID保存到一个数组中($oPIDs)
     * 3.  用$oPIDs对数据库进行查询
     */

    private function getProductsByOrder($oProducts)
    {
        $oPIDs = [];
        foreach ($oProducts as $item) {
            // $item  为单件商品的信息 
            array_push($oPIDs, $item['product_id']);
            // 使用 array_push 保存到 $oPIDs 数组中
        }

        //  @visible()  需显示的字段
        //  @toArray()   把结果转换为数组
        $products = ModelProduct::all($oPIDs)->visible(['id', 'price', 'stock', 'name', 'main_img_url'])->toArray();
        return $products;
    }
}